package com.chenwc.util.fileutil;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * 写文件
 *
 * @author chenwc
 * @date 2024/4/1 22:23
 */
public class WriteFile {

    private static final Logger log = LoggerFactory.getLogger(WriteFile.class);

    /**
     * 以UTF-8字符编码写文件
     *
     * @param file 待写入文件
     * @param data 待写入数据
     */
    public static void write(File file, String data) {
        write(file, data, StandardCharsets.UTF_8);
    }

    /**
     * 以UTF-8字符编码写文件
     *
     * @param file     待写入文件
     * @param data     待写入数据
     * @param isAppend 是否追加
     */
    public static void write(File file, String data, boolean isAppend) {
        try {
            FileUtils.write(file, data, StandardCharsets.UTF_8, isAppend);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 写文件
     *
     * @param file    待写入文件
     * @param data    待写入数据
     * @param charset 字符编码
     */
    public static void write(File file, String data, Charset charset) {
        try {
            FileUtils.touch(file);
            FileUtils.write(file, data, charset);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 流交换操作，把流写入到文件
     *
     * @param ins     输入流
     * @param tarFile 待写入文件
     */
    private static void transferStream(InputStream ins, File tarFile) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 10);
        ReadableByteChannel rbcInst = Channels.newChannel(ins);
        //获取目标输出文件的fileChannel
        FileChannel fileChannel = null;
        FileOutputStream fos = null;
        log.info("写入文件路径: {}", tarFile.getAbsolutePath());
        try {
            FileUtils.touch(tarFile);
            long start = System.currentTimeMillis();
            log.info("开始写入文件............");
            fos = new FileOutputStream(tarFile);
            fileChannel = fos.getChannel();
            while (-1 != (rbcInst.read(byteBuffer))) {
                byteBuffer.flip();
                fileChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            long end = System.currentTimeMillis();
            log.info("写入文件成功............");
            log.info("写入文件耗时: {} ms", end - start);
        } catch (IOException ioe) {
            log.error("Error occurred: ", ioe);
        } finally {
            try {
                if (null != fos) {
                    fos.close();
                }
                if (null != rbcInst) {
                    rbcInst.close();
                }
                if (null != fileChannel) {
                    fileChannel.close();
                }
            } catch (IOException e) {
                log.error("Error occurred: ", e);
            }
        }
    }

    /**
     * 把 InputStream 流写入本地文件（字节流）
     *
     * @param inputStream InputStream 流
     * @param filePath    待写入本地文件全路径+文件名
     */
    public static void writeFileByInputStream(InputStream inputStream, String filePath) {
        File file = new File(filePath);
        log.debug("写入文件路径: {}", file.getAbsolutePath());
        OutputStream outputStream = null;
        try {
            //如果是目录，则创建目录并返回
            if (file.isDirectory()) {
                FileUtils.forceMkdir(file);
                return;
            } else {
                FileUtils.touch(file);
            }
            long start = System.currentTimeMillis();
            log.debug("开始写入文件............");

            outputStream = new BufferedOutputStream(Files.newOutputStream(file.toPath()));
            byte[] buffer = new byte[1024 * 10];
            int bytesRead;
            while ((bytesRead = readBytes(inputStream, buffer, 0, buffer.length)) > 0) {
                outputStream.write(buffer, 0, bytesRead);
            }
            long end = System.currentTimeMillis();
            log.debug("写入文件成功............");
            log.debug("写入文件耗时: {} ms", end - start);
        } catch (IOException e) {
            log.error("Error occurred: ", e);
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != outputStream) {
                    //保存数据
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("Error occurred: ", e);
            }
        }
    }

    /**
     * 读取 InputStream 流中的数据
     *
     * @param inputStream InputStream 流
     * @param b           待写入数据的二进制数组
     * @param off         读取的起始位置
     * @param len         读取的长度
     * @return 读取到的字节数
     * @throws IOException IO异常
     */
    public static int readBytes(InputStream inputStream, byte[] b, int off, int len) throws IOException {
        int n;
        int count;
        for (n = 0; n < len; n += count) {
            count = inputStream.read(b, off + n, len - n);
            if (count < 0) {
                break;
            }
        }

        return n;
    }

}